/*
 * @Description: 整点儿花活?
 * @Autor: lele
 * @Date: 2021-11-17 16:24:05
 * @FilePath: \zz-1111\src\components\js\state\watch.js
 * @LastEditTime: 2021-11-23 08:55:31
 * @note: 对于多页面的标识，用了当前时间戳，这也许不是最优解(对于客户端来说并没有影响)。
 * @demo: https://gitee.com/SimonaGo/corresponding-formula
 */
class watcher {
   constructor(obj, orData = null) {
      this.obj = obj;
      this.watchs = null;
      this.temArr = [];
      this.orData = orData; // 源数据已设为私有属性，不可更改
      this.curPage = Object.create(null); // 缓存当前页面的监听，便于手动清除缓存
      this.changeData = Object.create(null); // 数据变化缓存最新值
   }
   colletionObserve(targetChange) {
     /* 每次提交数据的变动可能并未有新值出现，比如一直提交21，则需要判断是否要触发对应的监听事件，因此这里需要对changeData进行代理操作，避免提交重复的值导致多次触发Observechange*/
     const that = this;
     this.changeData = new Proxy(that.changeData, {
      set: function (target, propKey, value, receiver) {
        if(Reflect.has(target, propKey)){
              if(Reflect.get(that.changeData, propKey) == value){
                  console.log('nothing to do ')
                  return false
              }else{
                Reflect.set(target, propKey, value);
                that.Observechange(targetChange);
                return true
              }
        }else{
          that.Observechange(targetChange)
          Reflect.set(target, propKey, value);
          return true
        }
      } // state 数据发生变动通知观察者
   });
    Reflect.set(this.changeData, targetChange.key, targetChange.val); // 总是缓存最新变动的值
   }
   Observechange(target) {
      let orValue = null,
         that = this,
         newval = null,
         curPage = this.curPage;
      try {
         that.temArr.forEach(item => {
            if (Array.isArray(target)) { // 首次触发的时机,非commit事件
               if (curPage.methods._state_ID == item.Id) { // 触发当前页面监听事件
                  for (let key of target) {
                     if (key == item.originKey) {
                        if (Reflect.has(that.orData, key)) { // 源数据中是否存在监听的Key值
                           if (Reflect.has(that.changeData, key)) { // 是否更新了值
                              newval = Reflect.get(that.changeData, key); // 获取最新的值
                           } else {
                              newval = Reflect.get(that.orData, key) // 返回原始数据
                           }
                           item.fn(Reflect.get(that.orData, key), newval) // 通知相关页面数据变动
                        } else {
                           throw new ReferenceError("Prop name \"" + key + "\" does not exist in state.");
                        }
                     }
                  }
               }
            } else {
               if (target.key == item.originKey) {
                  if (Reflect.has(that.orData, target.key)) {
                       orValue = Reflect.get(that.orData, target.key); // 更新旧值
                  }
                  item.fn(orValue, target.val) // 通知相关页面数据变动
               }
            }
         });
      } catch (e) {
         // forEach循环被终止了，优化查询比较，降低复杂度
         console.log(e)
      }
   }
   typeCheck(obj) {
      return typeof obj !== "object" ? typeof obj : Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
   }
   watch(params) {
      const timestamp = new Date().getTime(),
            keys = Reflect.ownKeys(params.methods);
      let fn = null,
         temObj = {};
      Reflect.set(params.methods, '_state_ID', timestamp); // 标识页面
      this.curPage = params // 缓存
      for (let key of keys) {
         if (typeof (key) != 'symbol' && key.substring(0, 7) != '_state_') {
            if (Reflect.has(this.orData, key)) {
               fn = params.methods[key]
               if (this.typeCheck(fn) == 'function') {
                  Reflect.set(temObj, 'originKey', key);
                  Reflect.set(temObj, 'Id', timestamp);
                  Reflect.set(temObj, 'fn', fn);
                  this.temArr.push(temObj);
                  temObj = {};
               } else {
                  console.error(`You can't watch ${key},because it is not a function`)
               }
            } else {
               console.error(`You can't watch ${key},because it is not be created in state`)
            }
         } else {
            try {
               throw new ReferenceError("You cannot set a key that begins with a '_state_'，because it's a protect property");
            } catch (error) {
               console.log(error)
            }
         }
      }
      this.Observechange(keys); // 开始监听
   }
   differ(pageWatch, tem) { // 找出两个数组的交集， 由于并没有异步的读取数据，在最坏情况下调用differ可以将时间复杂度从O(n²)降低到O(mn).其他情况下，时间复杂度一样
      let pageId = Reflect.get(pageWatch, '_state_ID')
      let temArr = [];
      let temLen = temArr.length;
      let pageLen = pageWatch.length;
      if (pageId) { // 是否已有标识
         if (tem) {
            let reId = tem.filter(res => res.Id == pageId) // 找到符合的元素并返回
            for (let item of reId) {
               temArr.push(item.originKey)
            }
         } else {
            return false // tem不存在 返回false
         }
      } else {
         return false // pageWatch不存在_state_ID 返回false
      }
      const set_intersection = (set1, set2) => {
         if (set1.size > set2.size) {
            return set_intersection(set2, set1);
         }
         const intersection = new Set();
         for (const num of set1) {
            if (set2.has(num)) {
               intersection.add(num);
            }
         }
         return [...intersection];
      }
      let intersection = function (nums1, nums2) {
         const set1 = new Set(nums1);
         const set2 = new Set(nums2);
         return set_intersection(set1, set2);
      };
      const intersectionArr = intersection(pageWatch, temArr)
      if (intersectionArr.length != temLen && intersectionArr.length != pageLen) {
         return false // 两次数组不一致 返回false
      }
      return true // 两次数组一致 返回true
   }
   clearWatchMemory() {
      const pageWatch = this.curPage;
      if (pageWatch == null) { // 如果当前页面没有pageWatch 说明还未有监听方法在执行或者入参错误
         try {
            throw new ReferenceError("You cannot run clearWatchMemory method，because the Memory is empty");
         } catch (error) {
            console.log(error)
         }
         return
      } else {
         let pageData = pageWatch.methods;
         let pageId = Reflect.get(pageData, '_state_ID')
         this.temArr = this.temArr.filter(res => res.Id != pageId) // 清除缓存
         this.curPage = Object.create(null)
      }
   }
}

exports.watcher = watcher